package com.gitee.luosl.cola.match.pattern;

import com.gitee.luosl.cola.lang.Tuple4;
import com.gitee.luosl.cola.lang.function.Predicate3;
import com.gitee.luosl.cola.lang.function.Predicate4;

import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * Created by luosl on 2021/03/30/9:46
 */
abstract public class Pattern4<T, T1, T2, T3, T4> implements Pattern<T, Tuple4<T1, T2, T3, T4>>{

    public static <T, T1, T2, T3, T4, R1, R2, R3, R4> Pattern4<T, R1, R2, R3, R4> of(
            Class<? super T> clazz,
            Predicate<T> predicate,
            Pattern<T1, R1> pattern1, Pattern<T2, R2> pattern2,
            Pattern<T3, R3> pattern3, Pattern<T4, R4> pattern4,
            Function<T, Tuple4<T1, T2, T3, T4>> unapply) {

        return new Pattern4<T, R1, R2, R3, R4>() {
            @Override
            public Tuple4<R1, R2, R3, R4> apply(T value) {
                return unapply.apply(value)
                        .map1(pattern1::apply)
                        .map2(pattern2::apply)
                        .map3(pattern3::apply)
                        .map4(pattern4::apply);
            }

            @Override
            public boolean isDefinedAt(T value) {
                Tuple4<T1, T2, T3, T4> unapplyValue = unapply.apply(value);
                return clazz.isInstance(value)
                        && predicate.test(value)
                        && pattern1.isDefinedAt(unapplyValue._1)
                        && pattern2.isDefinedAt(unapplyValue._2)
                        && pattern3.isDefinedAt(unapplyValue._3)
                        && pattern4.isDefinedAt(unapplyValue._4);
            }
        };

    }

    public static <T, T1, T2, T3, T4> Pattern4<T, T1, T2, T3, T4> of(Pattern<T, Tuple4<T1, T2, T3, T4>> pattern){
        return new Pattern4<T, T1, T2, T3, T4>() {
            @Override
            public boolean isDefinedAt(T value) {
                return pattern.isDefinedAt(value);
            }

            @Override
            public Tuple4<T1, T2, T3, T4> apply(T value) {
                return pattern.apply(value);
            }
        };
    }

}
